#!/bin/bash
#
# Author:   Dr. Mike Murphy (mmurphy2@coastal.edu)
# Revision: 14 November 2013
#
#   Copyright 2013 Coastal Carolina University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#


declare -g PACKAGE_SLACKWARE_SECTION="agent/package/slackware"
declare -g PACKAGE_SLACKWARE_CACHE="${P_CACHEDIR}/agent/package/slackware"


## p_agent_package_slackware_cache_package <url> <security provider>
##
## Downloads and verifies a Slackware package from <url>, storing the package
## file in the packages/slackware subdirectory of the Pulley cache directory.
## When complete, either a validated package file will be stored in the
## directory, or the downloaded file will be removed in case of an error.
## 
## Verification uses the specified <security provider> to ensure that the
## package is not corrupt. To skip verification, use the "none" provider.
##
## Returns 0 on success, non-zero if an error occurs.
##
function p_agent_package_slackware_cache_package() {
	local status=0
	local chk=0
	local pkgbase=$(basename "$1")
	local pkgfile="${PACKAGE_SLACKWARE_CACHE}/${pkgbase}"
	local url="$1"
	
	# Step 1: download the package
	if [ -n "$(echo ${url} | grep '.t[a-z]z$')" ]; then
		p_download_cache "${url}" "${PACKAGE_SLACKWARE_SECTION}"
		status=$?
	else
		status=1
		# Try .txz, then .tgz, then .tbz
		for xtn in txz tgz tbz; do
			url="$1.${xtn}"
			p_download_cache "${url}" "${PACKAGE_SLACKWARE_SECTION}"
			chk=$?
			
			if [[ ${chk} -eq 0 ]]; then
				status=0
				pkgbase="${pkgbase}.${xtn}"
				pkgfile="${PACKAGE_SLACKWARE_CACHE}/${pkgbase}"
				break
			fi
		done
	fi
	
	if [[ "$2" != "none" ]]; then
		# Step 2: download the .asc signature file (if package was successful)
		if [[ ${status} -eq 0 ]]; then
			p_download_cache "${url}.asc" "${PACKAGE_SLACKWARE_SECTION}"
			status=$?
		fi
	
		# Step 3: if success so far, verify the package signature
		if [[ ${status} -eq 0 ]]; then
			psec_verify_signature "${PACKAGE_SLACKWARE_SECTION}" "$2" \
				"${pkgfile}.asc" "${pkgfile}"
			#
			status=$?
		fi
	fi
	
	# Step 4: if success, clean up the .asc file; otherwise clean up any
	# failed download files
	if [[ ${status} -eq 0 ]]; then
		rm -f "${pkgfile}.asc"
	else
		rm -f "${pkgfile}" "${pkgfile}.asc"
	fi
	
	return ${status}
}


## p_agent_package_slackware_check_package <package name>
##
## Checks to see if a package by the name of <package name> is already
## installed on the Slackware system. Returns 0 if the package is installed,
## or 100 if no record of the package installation is found.
##
function p_agent_package_slackware_check_package() {
	local basepkgname=
	local status=0
	
	# Packages can be listed with sections before them, e.g. a/aaa_base
	basepkgname=$(basename "$1")
	
	if [[ -n "${basepkgname}" ]]; then
		# Strip off any package extension before testing
		basepkgname=$(echo "${basepkgname}" | sed s/.t[a-z]z$//)
		if [[ ! -f "${P_ROOT}/var/log/packages/${basepkgname}" ]]; then
			status=100
		fi
	fi
	
	return ${status}
}


## p_agent_package_slackware_cache_missing
##
## Caches packages for installation or upgrade, using the packages requested
## in the subscription. Stops on the first error. Returns 0 on success,
## non-zero on failure.
##
function p_agent_package_slackware_cache_missing() {
	local status=0
	local chk=0
	local pkgname=
	local pkgfrom=
	local pkgurl=
	local provider=
	local check_provider=
	local repoentry=
	
	for entry in "${P_AGENT_PKGLIST[@]}"; do
		# GPG is the default security provider for Slackware packages
		provider=gpg
		
		# Extract the package name and repository from the array entry, then
		# check to see if the package is already installed.
		pkgname=$(extract_element "name" "${entry}")
		pkgfrom=$(extract_element "from" "${entry}")
		
		p_agent_package_slackware_check_package "${pkgname}"
		chk=$?
		
		# If the package is not currently installed, determine the repository
		# from which to obtain it.
		if [[ ${chk} -eq 100 ]]; then
			
			if [[ -n "${pkgfrom}" ]]; then
				# If the repository is specified along with the package name,
				# look up the repository entry in the P_AGENT_PACKAGE_REPOLIST
				# array.
				repoentry=$(find_entry "id" "${pkgfrom}" \
					"${P_AGENT_PACKAGE_REPOLIST[@]}")
				#
				
				if [[ -n "${repoentry}" ]]; then
					# If there is a repository entry, need to get the URL
					pkgurl=$(extract_element "url" "${repoentry}")
					if [[ -n "${pkgurl}" ]]; then
						# URL found: append the package name to get the full
						# download URL for the package.
						pkgurl="${pkgurl}/${pkgname}"
						
						# Does this repository specify a different security provider?
						check_provider=$(extract_element "security" "${repoentry}")
						if [[ -n "${check_provider}" ]]; then
							provider="${check_provider}"
						fi
					else
						# Repository URL is missing: error
						echo "No URL provided for repository: ${pkgfrom}"
						status=2
					fi
				else
					# Repository undefined: error
					echo "Package requested from repository: ${pkgfrom}"
					echo "No such repository defined."
					status=2
				fi
			else
				# No --from was specified in the add_package directive, so
				# either the package name is already a full URL, or a failure
				# is going to occur later.
				echo "No repository defined for ${pkgname}"
				pkgurl="${pkgname}"
			fi
		fi
		
		if [[ ${status} -eq 0 ]]; then
			# Everying OK so far: try to cache the package
			p_agent_package_slackware_cache_package "${pkgurl}" "${provider}"
			status=$?
		fi
		
		if [[ ${status} -ne 0 ]]; then
			# Error occurred: fail and stop processing
			echo "Failed to download and verify: ${pkgurl}"
			break
		fi
	done
	
	return ${status}
}


## p_agent_package_slackware_import_keys
##
## Imports GPG keys from defined Slackware "repositories". Returns 0 on
## success, non-zero on failure. Stops on error.
##
function p_agent_package_slackware_import_keys() {
	local keyurl=
	local keyfile=
	local provider=
	local check_provider=
	local status=0
	
	# Iterate through each repository and import all the GPG keys
	for entry in "${P_AGENT_PACKAGE_REPOLIST[@]}"; do
		keyurl=$(extract_element "pubkey" "${entry}")
		
		provider=gpg
		check_provider=$(extract_element "security" "${entry}")
		if [[ -n "${check_provider}" ]]; then
			provider="${check_provider}"
		fi
		
		if [[ -n "${keyurl}" ]]; then
			keyfile=$(p_download_temp "${keyurl}")
			status=$?
			
			if [[ ${status} -eq 0 ]]; then
				psec_import_key "${PACKAGE_SLACKWARE_SECTION}" "${provider}" \
					"${keyfile}"
				#
				status=$?
			fi
			
			if [[ ${status} -ne 0 ]]; then
				break
			fi
		fi
	done
	
	return ${status}
}


## p_agent_package_slackware_check
##
## Compares the list of desired packages to the set of installed packages.
## Returns 100 if any desired package in the list is not installed, otherwise
## returns 0.
##
function p_agent_package_slackware_check() {
	local pkgname=
	local status=0
	
	for entry in "${P_AGENT_PKGLIST[@]}"; do
		pkgname=$(extract_element "name" "${entry}")
		p_agent_package_slackware_check_package "${pkgname}"
		status=$?
		
		if [[ ${status} -ne 0 ]]; then
			# Package not found: short-circuit here and return 100, without
			# cycling through the rest of the package list.
			break
		fi
	done
	
	return ${status}
}


## p_agent_package_slackware_run
##
## Implements the Slackware platform-specific logic required to update
## and install packages. Returns 0 on success, non-zero if an error occurs.
##
function p_agent_package_slackware_run() {
	local status=0
	
	# Need the GPG keys to be imported before trying to download packages
	p_agent_package_slackware_import_keys
	status=$?
	
	# If GPG import is successful, download whatever packages are missing
	# into the cache
	if [[ ${status} -eq 0 ]]; then
		p_agent_package_slackware_cache_missing
		status=$?
	fi
	
	# If downloads are good, install/upgrade using upgradepkg --install-new
	# Note that we need another form of the command if working on an external
	# root directory.
	if [[ ${status} -eq 0 ]]; then
		if [[ -n "${P_ROOT}" ]]; then
			ROOT="${P_ROOT}" upgradepkg --install-new \
				"${PACKAGE_SLACKWARE_CACHE}/*.t?z"
			#
			status=$?
		else
			upgradepkg --install-new "${PACKAGE_SLACKWARE_CACHE}/*.t?z"
			status=$?
		fi
	fi
	
	# Only clean up the cached packages if everything succeeded. This way, a
	# single failure doesn't result in having to re-download the packages on
	# the next Pulley run.
	if [[ ${status} -eq 0 ]]; then
		rm -f "${PACKAGE_SLACKWARE_CACHE}/*.t?z"
	fi
	
	return ${status}
}
